/*
*
* Copyright (c) 2011 Vaulting Systems International
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy 
* of this software and associated documentation files (the "Software"), to deal 
* in the Software without restriction, including without limitation the rights 
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
* of the Software, and to permit persons to whom the Software is furnished to do  
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all  
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE  
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.ekeyman.client.test;

import java.security.SecureRandom;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

import com.ekeyman.client.service.EkeymanService;
import com.ekeymanlib.dto.AppDeviceApiKeys;
import com.ekeymanlib.dto.EncryptionKeys;
import com.ekeymanlib.dto.VendorProfile;

public class EkeyManWsPbeUT {
	
	private static final String[] SPRING_CONFIG_FILES = new String[]{"applicationContext.xml"};
	private EkeymanService ekeymanService;
	ClassPathXmlApplicationContext ctx;
	
	@Parameters({"openidurl","apiKey","emailAddress","application","device","resourceUri"}) 
	@Test
	public void testEnd2End(
			@Optional("http://my.yahoo.com/openid/jimmy110/") String openidurl,
			@Optional("") String apiKey,
			@Optional("james.bell01@estateplusplus.com") String emailAddress,
			@Optional("HighlySecureAndroidFinancialApp") String application,
			@Optional("704-942-7257") String device,
			@Optional("BillPayTable/AccountNumberColumn") String resourceUri) {

		System.out.println("Entered testEnd2End...");
		

		if(apiKey == null || apiKey.equalsIgnoreCase("")){
			apiKey = getEkeymanService().getUser(openidurl);
			if(apiKey == null || apiKey.equalsIgnoreCase("")){
				apiKey = getEkeymanService().registerVendor(
						"Jimmy Bell", 
						"4339 Castleton Road", 
						"Charlotte", 
						"NC", 
						"28211", 
						"704-942-7257", 
						emailAddress);
				getEkeymanService().registerUser(apiKey, openidurl);
			}
		}	
		
		VendorProfile vendorProfile = getEkeymanService().getVendorProfile(apiKey);
	    assert vendorProfile != null : "Expected vendorProfile to be not null";
		
		AppDeviceApiKeys appDeviceApiKeys = getEkeymanService().registerAppDevice(
				apiKey, application, device);
		
		String location = "Current cellphone GPS coordinates for createEncryptionKeys";

		String secretKeyType = "PBEWITHSHA256AND256BITAES-CBC-BC"; //javax.crypto.KeyGenerator algorithm name
		int ivLength = 16; // in bytes
		int saltLength = 32; // in bytes
        int keyLength = 1024; 
		int iterationCountMax = 2048;
		String passphrase = "The quick brown fox jumped over the lazy brown dog"; 
        String originalText = "hello world"; 
        
        System.out.println("originalText: " + originalText);

	    try {
					
	    	EncryptionKeys keysToEncrypt = getEkeymanService().createEncryptionKeys(resourceUri, 
	    			appDeviceApiKeys.getPublicKey(), appDeviceApiKeys.getPrivateKey(), 
	    			16, ivLength, saltLength, iterationCountMax, location);
		    
	    	SecureRandom random = new SecureRandom();
	        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(secretKeyType); 
	        
	        // encrypt
	        PBEKeySpec eKeySpec = new PBEKeySpec(passphrase.toCharArray(), keysToEncrypt.getSaltBytes(), keysToEncrypt.getIterationCount(), keyLength); 

	        SecretKey eKey = keyFactory.generateSecret(eKeySpec); 
	  
	        Cipher eCipher = Cipher.getInstance("AES/CTR/NOPADDING"); 
	        
	        IvParameterSpec eIvParameterSpec =  new IvParameterSpec(keysToEncrypt.getIvBytes()); 

	        eCipher.init(Cipher.ENCRYPT_MODE, eKey, eIvParameterSpec, random); 
	        byte [] ciphertext = eCipher.doFinal(originalText.getBytes()); 
	        System.out.println("input text : " + new String(originalText));
	    
	        // descrypt
			EncryptionKeys keysToDecrypt = getEkeymanService().getEncryptionKeys(resourceUri, 
					appDeviceApiKeys.getPublicKey(), appDeviceApiKeys.getPrivateKey(), location);
			
	        PBEKeySpec dKeySpec = new PBEKeySpec(passphrase.toCharArray(), keysToDecrypt.getSaltBytes(), keysToDecrypt.getIterationCount(), keyLength); 
	        
	        SecretKey dKey = keyFactory.generateSecret(dKeySpec); 
	 
	        Cipher dCipher = Cipher.getInstance("AES/CTR/NOPADDING"); 
	        
	        IvParameterSpec dIvParameterSpec =  new IvParameterSpec(keysToDecrypt.getIvBytes()); 

	        dCipher.init(Cipher.DECRYPT_MODE, dKey, dIvParameterSpec, random); 
	        String decryptedText = new String(dCipher.doFinal(ciphertext)); 
	        System.out.println("decryptedText: " + decryptedText);
		    
		    // At this point the original text and decrypted text should match!!!!
		    assert originalText.equals(decryptedText) : "Expected originalText and decrypted text to match" + 
		    		" originalText: " + originalText + " decryptedText: " + decryptedText;
		    
		    getEkeymanService().deleteVendor(apiKey);
	    }
	    catch (Exception e){
	    	System.out.println(e.getMessage());
	    }
	    System.out.println("Exited testEnd2End...");
	}
	
	@Parameters({"openidurl","apiKey","emailAddress","application","device","resourceUri"}) 
	@Test
	public void testGenerateNewPrivateKey(
			@Optional("http://my.yahoo.com/openid/jimmy620/") String openidurl,
			@Optional("") String apiKey,
			@Optional("james.bell62@estateplusplus.com") String emailAddress,
			@Optional("HighlyInsecureAndroidFinancialApp") String application,
			@Optional("312-474-8645") String device,
			@Optional("BillPayTable/AccountNumberColumn") String resourceUri) {
		
		System.out.println("Entered testGenerateNewPrivateKey...");
		
		if(apiKey == null || apiKey.equalsIgnoreCase("")){
			apiKey = getEkeymanService().getUser(openidurl);
			if(apiKey == null || apiKey.equalsIgnoreCase("")){
				apiKey = getEkeymanService().registerVendor(
						"Jimmy Bell", 
						"4339 Castleton Road", 
						"Charlotte", 
						"NC", 
						"28211", 
						"704-942-7257", 
						emailAddress);
				getEkeymanService().registerUser(apiKey, openidurl);
			}
		}	
		
		VendorProfile vendorProfile = getEkeymanService().getVendorProfile(apiKey);
	    assert vendorProfile != null : "Expected vendorProfile to be not null";
		
		AppDeviceApiKeys appDeviceApiKeys = getEkeymanService().registerAppDevice(
				apiKey, application, device);
		
		System.out.println("public key: " + appDeviceApiKeys.getPublicKey() +
				" private key: " + appDeviceApiKeys.getPrivateKey());
		
		String location = "Current cellphone GPS coordinates for createEncryptionKeys";
		
		String secretKeyType = "PBEWITHSHA256AND256BITAES-CBC-BC"; //javax.crypto.KeyGenerator algorithm name
		int ivLength = 16; // in bytes
		int saltLength = 32; // in bytes
        int keyLength = 1024; 
		int iterationCountMax = 2048;
		String passphrase = "The quick brown fox jumped over the lazy brown dog"; 
        String originalText = "hello world"; 

	    try {
					
	    	EncryptionKeys keysToEncrypt = getEkeymanService().createEncryptionKeys(resourceUri, 
	    			appDeviceApiKeys.getPublicKey(), appDeviceApiKeys.getPrivateKey(), 
	    			16, ivLength, saltLength, iterationCountMax, location);
		    
	    	SecureRandom random = new SecureRandom();
	        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(secretKeyType); 
	        
	        // encrypt
	        PBEKeySpec eKeySpec = new PBEKeySpec(passphrase.toCharArray(), keysToEncrypt.getSaltBytes(), keysToEncrypt.getIterationCount(), keyLength); 

	        SecretKey eKey = keyFactory.generateSecret(eKeySpec); 
	  
	        Cipher eCipher = Cipher.getInstance("AES/CTR/NOPADDING"); 
	        
	        IvParameterSpec eIvParameterSpec =  new IvParameterSpec(keysToEncrypt.getIvBytes()); 

	        eCipher.init(Cipher.ENCRYPT_MODE, eKey, eIvParameterSpec, random); 
	        byte [] ciphertext = eCipher.doFinal(originalText.getBytes()); 
	        System.out.println("input text : " + new String(originalText));	
		    
		    // Change private key and try to decrypt 
		    AppDeviceApiKeys changedAppDeviceApiKeys = getEkeymanService().generateNewPrivateKey(appDeviceApiKeys.getPublicKey(), apiKey);
		    
		    System.out.println("generateNewPrivateKey...");
			System.out.println("public key: " + changedAppDeviceApiKeys.getPublicKey() +
					" private key: " + changedAppDeviceApiKeys.getPrivateKey());

			// Need to test that private key changed
		    assert !(appDeviceApiKeys.getPrivateKey().equals(changedAppDeviceApiKeys.getPrivateKey())) : 
		    		"Expected appDeviceApiKeys.getPrivateKey() and changedAppDeviceApiKeys.getPrivateKey() to be different" + 
		    		" appDeviceApiKeys.getPrivateKey(): " + appDeviceApiKeys.getPrivateKey() + 
		    		" changedAppDeviceApiKeys.getPrivateKey(): " + changedAppDeviceApiKeys.getPrivateKey();

			
			// Use old private key as client doesn't know it has changed
			EncryptionKeys keysToDecrypt = getEkeymanService().getEncryptionKeys(resourceUri, 
					appDeviceApiKeys.getPublicKey(), appDeviceApiKeys.getPrivateKey(), location);
			
	        PBEKeySpec dKeySpec = new PBEKeySpec(passphrase.toCharArray(), keysToDecrypt.getSaltBytes(), keysToDecrypt.getIterationCount(), keyLength); 
	        
	        SecretKey dKey = keyFactory.generateSecret(dKeySpec); 
	 
	        Cipher dCipher = Cipher.getInstance("AES/CTR/NOPADDING"); 
	        
	        IvParameterSpec dIvParameterSpec =  new IvParameterSpec(keysToDecrypt.getIvBytes()); 

	        dCipher.init(Cipher.DECRYPT_MODE, dKey, dIvParameterSpec, random); 
	        String decryptedText = new String(dCipher.doFinal(ciphertext)); 
	        System.out.println("decryptedText: " + decryptedText);
	        
		    // At this point the original text and decrypted text should match!!!!
		    assert !(originalText.equals(decryptedText)) : "Expected originalText and decrypted text to be different" + 
		    		" originalText: " + originalText + " decryptedText: " + decryptedText;
		    
		    getEkeymanService().deleteVendor(apiKey);
	    }
	    catch (Exception e){
	    	System.out.println(e.getMessage());
	    }
		System.out.println("Exited testGenerateNewPrivateKey...");
	}
	
	@BeforeSuite
	public void beforeSuite() {
		System.out.println("Before suite...");
		
		ctx = new ClassPathXmlApplicationContext(SPRING_CONFIG_FILES);
		
		setEkeymanService((EkeymanService)ctx.getBeanFactory().getBean("serviceClient"));
		
		// Bouncy Castle 1.4.6 jars installed in JAVA_HOME/jre/lib/ext
		// Service provider added to JAVA_HOME/jre/lib/security/java.security
		// security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
	}
	
	@AfterSuite
	public void afterSuite() {
		System.out.println("After suite...");
	}
	
	public void setEkeymanService(EkeymanService ekeymanService) {
		this.ekeymanService = ekeymanService;
	}
	public EkeymanService getEkeymanService() {
		return ekeymanService;
	}



}
